﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
using System.Net.NetworkInformation;
using Microsoft.Win32;
using System.DirectoryServices;
using System.Runtime.InteropServices;
using System.IO;

namespace AdvancedRunAsClasses
{

    public static class AdvancedRunAsChecks
    {
        //srvr = ldap server, e.g. LDAP://domain.com
        //usr = user name
        //pwd = user password
        public static bool IsAuthenticated(string srvr, string usr, string pwd)
        {
            bool authenticated = false;
            try
            {
                DirectoryEntry entry = new DirectoryEntry(srvr, usr, pwd);
                object nativeObject = entry.NativeObject;
                authenticated = true;
            }
            catch (DirectoryServicesCOMException cex)
            {
                //not authenticated; reason why is in cex
            }
            catch (Exception ex)
            {
                //not authenticated due to some other exception [this is optional]
            }

            return authenticated;
        }    
    }
    
    public class RegData
    {
        private static string encIV = String.Empty;
        private static string encKey = String.Empty;
        //private static string encUser = String.Empty;
        //private static string encDomain = String.Empty;
        private static string encPass = String.Empty;

        private static string clrUser = String.Empty;
        private static string clrDomain = String.Empty;
        private static string clrPass = String.Empty;
        private static string clrPath = String.Empty;
        
        private static Cryptor crypt;
                
        public string User
        {
            get { return clrUser; }
            set {
                clrUser = value;
//                encUser = crypt.Encrypt(clrUser);
            } 
        }

        public string Password
        {
            get { return clrPass; }
            set
            {
                clrPass = value;
                encPass = crypt.Encrypt(clrPass);
            }
        }

        public string Domain
        {
            get { return clrDomain; }
            set {
                clrDomain = value;
//                encPass = crypt.Encrypt(clrDomain);
            }
        }

        public string Path
        {
            get { return clrPath; }
            set { clrPath = value; }
        }

        public RegData()
        {
            crypt = new Cryptor();
        }

        public bool IsInitCorrect
        {
            get { return false; }
        }

        /// <summary>
        /// Read data from the registry and check consistency
        /// </summary>
        /// <returns>0 - Succes; 1 - RegKey not found; 2 - IV/Key hashes changed</returns>
        public int ReadRegistry()
        {
            RegistryKey rkey = Registry.CurrentUser.OpenSubKey("Software\\AdvancedRunAs");
            if (rkey == null) { return 1; }
            clrUser = Convert.ToString(rkey.GetValue("User"));
            clrDomain = Convert.ToString(rkey.GetValue("Domain"));
            clrPath = Convert.ToString(rkey.GetValue("Path"));
            encPass = Convert.ToString(rkey.GetValue("Password"));
            encIV = Convert.ToString(rkey.GetValue("hash1"));
            encKey = Convert.ToString(rkey.GetValue("hash2"));
            if (DecryptData()) { return 0; }
            return 2;
        }

        /// <summary>
        /// Writes data to registry
        /// </summary>
        /// <returns>0 - Succes; 1 - RegKey error;</returns>
        public int WriteRegistry()
        {
            encIV = crypt.hashIV;
            encKey = crypt.hashKey;

            RegistryKey rkey = Registry.CurrentUser.CreateSubKey("Software\\AdvancedRunAs");
            if (rkey == null) { return 1; }

            rkey.SetValue("User", clrUser);
            rkey.SetValue("Domain", clrDomain);
            rkey.SetValue("Path", clrPath);
            rkey.SetValue("Password", encPass);
            rkey.SetValue("hash1", encIV);
            rkey.SetValue("hash2", encKey);
            return 0;
        }

        private bool DecryptData()
        {
            if ( (encIV != crypt.hashIV) || (encKey != crypt.hashKey) )
            {
                return false;
            }
//            clrUser = crypt.Decrypt(encUser);
//            clrDomain = crypt.Decrypt(encDomain);
            clrPass = crypt.Decrypt(encPass);
            return true;
        }
    }
    
    public class Cryptor
    {

        private static string aesKey = String.Empty;
        private static string aesInitVector = String.Empty;
        private static int aesBlockSize = 128;
        private static int aesKeySize = 256;
        private static PaddingMode aesPadding = PaddingMode.PKCS7;
        private static CipherMode aesMode = CipherMode.CBC;
                
        public bool Ready
        {
            get { return IsReady(); }
        }

        public string hashIV
        { get { return Encrypt(aesInitVector); } }

        public string hashKey
        { get { return Encrypt(aesKey); } }


        static Cryptor()
        {
            Init();
        }

        public string Encrypt(string Text)
        {
            byte[] plainTextBytes = System.Text.ASCIIEncoding.ASCII.GetBytes(Text);
            AesCryptoServiceProvider aes = new AesCryptoServiceProvider();
            aes.BlockSize = aesBlockSize;
            aes.KeySize = aesKeySize;
            aes.Key = System.Text.ASCIIEncoding.ASCII.GetBytes(aesKey);
            aes.IV = System.Text.ASCIIEncoding.ASCII.GetBytes(aesInitVector);
            aes.Padding = aesPadding;
            aes.Mode = aesMode;
            ICryptoTransform crypto = aes.CreateEncryptor();
            byte[] encrypted = crypto.TransformFinalBlock(plainTextBytes, 0, plainTextBytes.Length);
            crypto.Dispose();
            return Convert.ToBase64String(encrypted);
        }

        public string Decrypt(string encrypted)
        {
            byte[] encryptedBytes = Convert.FromBase64String(encrypted);
            AesCryptoServiceProvider aes = new AesCryptoServiceProvider();
            aes.BlockSize = aesBlockSize;
            aes.KeySize = aesKeySize;
            aes.Key = System.Text.ASCIIEncoding.ASCII.GetBytes(aesKey);
            aes.IV = System.Text.ASCIIEncoding.ASCII.GetBytes(aesInitVector);
            aes.Padding = aesPadding;
            aes.Mode = aesMode;
            ICryptoTransform crypto = aes.CreateDecryptor();
            byte[] plainTextBytes = crypto.TransformFinalBlock(encryptedBytes, 0, encryptedBytes.Length);
            crypto.Dispose();
            return System.Text.ASCIIEncoding.ASCII.GetString(plainTextBytes);
        }

        private static void Init()
        {
            NetworkInterface[] nics = NetworkInterface.GetAllNetworkInterfaces();
            String sMacAddress = string.Empty;
            String macAddressGuid = string.Empty;
            foreach (NetworkInterface adapter in nics)
            {
                if (sMacAddress == String.Empty)
                {
                    if (adapter.NetworkInterfaceType == System.Net.NetworkInformation.NetworkInterfaceType.Ethernet ||
                        adapter.NetworkInterfaceType == System.Net.NetworkInformation.NetworkInterfaceType.Wireless80211)
                    {
                        sMacAddress = adapter.GetPhysicalAddress().ToString();
                        macAddressGuid = adapter.Id;
                    }
                }
            }

            aesKey = macAddressGuid;
            
            if (aesKey.Length > 32)
            {
                aesKey = aesKey.Substring(aesKey.Length - 33, 32);
            }

            aesInitVector = sMacAddress + aesKey.Substring(aesKey.Length - 4, 4);


        }

        private static bool IsReady()
        {
            if (aesInitVector.Length != 16)     { return false; }
            if (aesKey.Length != 32)             { return false; }
            return true;
        }
    }
}

namespace Impersonation
{
    public static class Impersonator
    {
        #region "CONTS"


        const UInt32 INFINITE = 0xFFFFFFFF;
        const UInt32 WAIT_FAILED = 0xFFFFFFFF;

        #endregion

        #region "ENUMS"

        [Flags]
        public enum LogonType
        {
            LOGON32_LOGON_INTERACTIVE = 2,
            LOGON32_LOGON_NETWORK = 3,
            LOGON32_LOGON_BATCH = 4,
            LOGON32_LOGON_SERVICE = 5,
            LOGON32_LOGON_UNLOCK = 7,
            LOGON32_LOGON_NETWORK_CLEARTEXT = 8,
            LOGON32_LOGON_NEW_CREDENTIALS = 9
        }

        [Flags]
        public enum LogonProvider
        {
            LOGON32_PROVIDER_DEFAULT = 0,
            LOGON32_PROVIDER_WINNT35,
            LOGON32_PROVIDER_WINNT40,
            LOGON32_PROVIDER_WINNT50
        }

        #endregion

        #region "STRUCTS"

        [StructLayout(LayoutKind.Sequential)]
        public struct STARTUPINFO
        {
            public Int32 cb;
            public String lpReserved;
            public String lpDesktop;
            public String lpTitle;
            public Int32 dwX;
            public Int32 dwY;
            public Int32 dwXSize;
            public Int32 dwYSize;
            public Int32 dwXCountChars;
            public Int32 dwYCountChars;
            public Int32 dwFillAttribute;
            public Int32 dwFlags;
            public Int16 wShowWindow;
            public Int16 cbReserved2;
            public IntPtr lpReserved2;
            public IntPtr hStdInput;
            public IntPtr hStdOutput;
            public IntPtr hStdError;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct PROCESS_INFORMATION
        {
            public IntPtr hProcess;
            public IntPtr hThread;
            public Int32 dwProcessId;
            public Int32 dwThreadId;
        }

        #endregion

        #region "FUNCTIONS (P/INVOKE)"

        [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
        public static extern Boolean LogonUser
        (
            String lpszUserName,
            String lpszDomain,
            String lpszPassword,
            LogonType dwLogonType,
            LogonProvider dwLogonProvider,
            out IntPtr phToken
        );

        [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern Boolean CreateProcessAsUser
        (
            IntPtr hToken,
            String lpApplicationName,
            String lpCommandLine,
            IntPtr lpProcessAttributes,
            IntPtr lpThreadAttributes,
            Boolean bInheritHandles,
            Int32 dwCreationFlags,
            IntPtr lpEnvironment,
            String lpCurrentDirectory,
            ref STARTUPINFO lpStartupInfo,
            out PROCESS_INFORMATION lpProcessInformation
        );

        [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern Boolean CreateProcessWithLogonW
        (
            String lpszUsername,
            String lpszDomain,
            String lpszPassword,
            Int32 dwLogonFlags,
            String applicationName,
            String commandLine,
            Int32 creationFlags,
            IntPtr environment,
            String currentDirectory,
            ref STARTUPINFO sui,
            out PROCESS_INFORMATION processInfo
        );

        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern UInt32 WaitForSingleObject
        (
            IntPtr hHandle,
            UInt32 dwMilliseconds
        );

        [DllImport("kernel32", SetLastError = true)]
        public static extern Boolean CloseHandle(IntPtr handle);

        #endregion

        #region "FUNCTIONS"

        public static bool LaunchNetOnly(string strCommand, string strDomain, string strName, string strPassword)
        {
            return LaunchNetOnly(strCommand, strDomain, strName, strPassword, null);
        }

        public static bool LaunchNetOnly(string strCommand, string strDomain, string strName, string strPassword, string strWorkingDirectory)
        {
            // Variables
            PROCESS_INFORMATION processInfo = new PROCESS_INFORMATION();
            STARTUPINFO startInfo = new STARTUPINFO();
            bool bResult = false;
            UInt32 uiResultWait = WAIT_FAILED;
            int LOGON_NETCREDENTIALS_ONLY = 2;

            try
            {
                // Create process
                startInfo.cb = Marshal.SizeOf(startInfo);

                bResult = CreateProcessWithLogonW(
                    strName,
                    strDomain,
                    strPassword,
                    LOGON_NETCREDENTIALS_ONLY,
                    null,
                    strCommand,
                    0,
                    IntPtr.Zero,
                    strWorkingDirectory,
                    ref startInfo,
                    out processInfo
                );
                if (!bResult) { throw new Exception("CreateProcessWithLogonW error #" + Marshal.GetLastWin32Error().ToString()); }

                    uiResultWait = WaitForSingleObject(processInfo.hProcess, INFINITE);
                if (uiResultWait == WAIT_FAILED) { throw new Exception("WaitForSingleObject error #" + Marshal.GetLastWin32Error()); }

            }
            catch (System.Exception ex)
            {
                return false;
            }
            finally
            {
                // Close all handles
                CloseHandle(processInfo.hProcess);
                CloseHandle(processInfo.hThread);
            }
            return true;
        }


        public static void LaunchImpersonate(string strCommand, string strDomain, string strName, string strPassword)
        {
            // Variables
            PROCESS_INFORMATION processInfo = new PROCESS_INFORMATION();
            STARTUPINFO startInfo = new STARTUPINFO();
            Boolean bResult = false;
            IntPtr hToken = IntPtr.Zero;
            UInt32 uiResultWait = WAIT_FAILED;

            try
            {
                // Logon user
                bResult = Impersonator.LogonUser(
                    strName,
                    strDomain,
                    strPassword,
                    Impersonator.LogonType.LOGON32_LOGON_INTERACTIVE,
                    Impersonator.LogonProvider.LOGON32_PROVIDER_DEFAULT,
                    out hToken
                );
                if (!bResult) { throw new Exception("Logon error #" + Marshal.GetLastWin32Error()); }

                // Create process
                startInfo.cb = Marshal.SizeOf(startInfo);
                startInfo.lpDesktop = "winsta0\\default";

                bResult = Impersonator.CreateProcessAsUser(
                    hToken,
                    null,
                    strCommand,
                    IntPtr.Zero,
                    IntPtr.Zero,
                    false,
                    0,
                    IntPtr.Zero,
                    null,
                    ref startInfo,
                    out processInfo
                );
                if (!bResult) { throw new Exception("CreateProcessAsUser error #" + Marshal.GetLastWin32Error()); }

                // Wait for process to end
                uiResultWait = WaitForSingleObject(processInfo.hProcess, INFINITE);
                if (uiResultWait == WAIT_FAILED) { throw new Exception("WaitForSingleObject error #" + Marshal.GetLastWin32Error()); }
            }
            finally
            {
                // Close all handles
                CloseHandle(hToken);
                CloseHandle(processInfo.hProcess);
                CloseHandle(processInfo.hThread);
            }
        }

        #endregion
    }
}